package com.hup.joystick.service;

import com.hup.joystick.model.DeviceConfig;
import com.hup.joystick.model.joystick.AutoConnectConfig;
import com.hup.joystick.service.jna.DInputHResultException;
import com.hup.joystick.service.jna.DeviceStateCallback;
import com.hup.joystick.service.jna.EnumDeviceCallback;
import com.hup.joystick.service.jna.EnumDeviceData;
import com.hup.joystick.service.jna.JoystickJnaLib;
import com.hup.joystick.service.joystick.AutoConnectListener;
import com.hup.joystick.service.joystick.JoyEventListener;
import com.hup.joystick.service.joystick.JoystickService;
import com.hup.utils.commons.exception.AppCommonException;
import com.hup.utils.commons.start.SpringContext;
import lombok.extern.log4j.Log4j2;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author hugan
 * @date 2020/10/16
 */
@Log4j2
public class JoystickServiceJna extends JoystickService implements DeviceStateCallback {
    private static final JoystickJnaLib INSTANCE = JoystickJnaLib.INSTANCE;

    public JoystickServiceJna() {
        try {
            log.info("dllVersion={}", INSTANCE.version());
        } catch (Throwable e) {
            throw new IllegalArgumentException("dll加载异常:" + e.getMessage());
        }
    }

    @Override
    public void initDirectInput() throws DInputHResultException {
        log.debug("initDirectInput.st");
        boolean isUtf8 = StandardCharsets.UTF_8.equals(Charset.defaultCharset());
        DInputHResultException.testThrow("初始化DirectInput", INSTANCE.createDirectInput8(isUtf8, this));
    }

    /**
     * {@link JoystickJnaLib#enumDevices(EnumDeviceCallback)}
     */
    @Override
    public ArrayList<EnumDeviceData> enumDevices() throws DInputHResultException {
        notInUIThread();
        ArrayList<EnumDeviceData> res = new ArrayList<>();
        DInputHResultException.testThrow("获取设备列表", INSTANCE.enumDevices(res::add));
        return res;
    }

    /**
     * 经过JNA转换后的字符串 #str,如果乱码了,是再也转不回去的
     */
    @SuppressWarnings("unused")
    private static void printStr(String tag, String str) {
        log.info("{}={}", tag, str);
        log.info("GBK>UTF8={}", new String(str.getBytes(Charset.forName("GBK")), StandardCharsets.UTF_8));
        log.info("UTF8>GBK={}", new String(str.getBytes(StandardCharsets.UTF_8), Charset.forName("GBK")));
    }

    /**
     * {@link JoystickJnaLib#startListenState(String, int, int, boolean)}
     */
    @Override
    public void startListenState(String guid, int stateScanSpan, int stickRange, JoyEventListener joyEventListener) {
        stopListenState(true);
        log.info("启动监听线程");
        stateExecutor.execute(() -> {
            log.info("listen.st");
            listeningState = true;
            JoystickServiceJna.this.joyEventListener = joyEventListener;
            try {
                INSTANCE.startListenState(guid, stateScanSpan, stickRange, false);
            } finally {
                JoystickServiceJna.this.joyEventListener = null;
                listeningState = false;
                log.info("listen.ed");
            }
        });
    }

    /**
     * {@link JoystickJnaLib#stopListenState()}
     */
    @Override
    public void stopListenState(boolean waitEndingThread) {
        log.debug("");
        INSTANCE.stopListenState();
        super.stopListenState(waitEndingThread);
    }

    @Override
    public void startAutoConnect(AutoConnectConfig connectConfig, AutoConnectListener connectListener) {
        stopAutoConnect(true);
        autoConnectExecutor.execute(() -> {
            log.info("autoConnect.st");
            autoConnecting = true;
            try {
                //获取手柄配置
                List<DeviceConfig> lstConfig = connectListener.getConfig();
                if (lstConfig.isEmpty()) throw new AppCommonException("无可用的手柄配置");

                ArrayList<EnumDeviceData> lstOnline = new ArrayList<>();
                while (autoConnecting) {
                    connectListener.onScanning();
                    DInputHResultException.testThrow("获取设备列表", INSTANCE.enumDevices(lstOnline::add));
                    //lstOnline.clear();//test
                    log.debug("online={}", lstOnline.size());

                    //查找设备
                    DeviceConfig toConnect = connectListener.findDevice(lstOnline, lstConfig);
                    lstOnline.clear();
                    if (toConnect == null) {
                        sleepScanDevice(connectConfig.deviceScanSpan);
                        continue;
                    }

                    //开始监听
                    try {
                        listeningState = true;
                        JoystickServiceJna.this.joyEventListener = connectListener;
                        connectListener.beforeConnect(toConnect);
                        SpringContext.getBean(AudioService.class).connected();
                        INSTANCE.startListenState(toConnect.guid, connectConfig.stateScanSpan, connectConfig.stickRange, true);
                    } finally {
                        listeningState = false;
                        JoystickServiceJna.this.joyEventListener = null;
                    }
                    //达到此处:设备断开
                    SpringContext.getBean(AudioService.class).disconnected();
                }
                //达到此处:调用了stop方法
                autoConnecting = false;
            } catch (Throwable e) {
                autoConnecting = false;
                connectListener.onException(e);
            }
            log.info("autoConnect.ed");
        });
    }

    @Override
    protected void doFreeDirectInput() {
        log.info("");
        INSTANCE.doFree();
    }

}
